package cop.publ;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import cop.internal.LayerWrapperManager;




/**
 * This class offers a set of static methods which can be used to modify the 
 * set of layers currently affecting the execution.
 * 
 * @author guido
 *
 */
public class Ctx {
	
	
	/* 
	 * The static block implements in a thread local manner the following code:
	 * 
	 * private static Stack<ArrayList<Layer>> activeLayers = 
	 *                         new Stack<ArrayList<Layer>>(); 
	 * static{activeLayers.push(new ArrayList<Layer>());}
	 */
	static final ThreadLocal<Stack<ArrayList<Layer>>> activeLayers 
		= new ThreadLocal<Stack<ArrayList<Layer>>>() {
			@Override
			protected Stack<ArrayList<Layer>> initialValue() {
				Stack<ArrayList<Layer>> tmp = new Stack<ArrayList<Layer>>();
				tmp.push(new ArrayList<Layer>());
				return tmp;
			}
		};
	
	
	/**
	 * Retruns a list containing the active layers at this point of the
	 * execution.
	 * 
	 * @return
	 */
	public static List<Layer> getActiveLayers(){
		return activeLayers.get().peek();
	}
	
	
	/*
	 * Optimize this !!
	 */
	public static void withActiveLayers(Iterable<Layer> ll){
		
		// Better to use Arrays
		ArrayList<Layer> al = new ArrayList<Layer>();
		for(Layer l : ll){
			al.add(l);
		}
		
		
		ArrayList<Layer> top = activeLayers.get().peek();
		ArrayList<Layer> newTop = (ArrayList<Layer>) top.clone();
		
		
		// Take those layers not already active
		ArrayList<Layer> notAlreadyActive = new ArrayList<Layer>();
		for(Layer l : al){
			if (!top.contains(l)){
				notAlreadyActive.add(l);
			}
		}
		
		
		if(notAlreadyActive.size() == 0){
			Stack<ArrayList<Layer>> tmp = activeLayers.get();
			tmp.push(newTop);
			activeLayers.set(tmp);
		}else{
			for(Layer l : notAlreadyActive){
				newTop.add(0,l);
			}	
			Stack<ArrayList<Layer>> tmp = activeLayers.get();
			tmp.push(newTop);
			activeLayers.set(tmp);
		}
		LayerWrapperManager.setActiveLayers(newTop);
		return;
	}
	
	
	/**
	 * Opens a block in which the layer l is added to the current active layer
	 * configuration. Does nothing if l is already active.</br></br>
	 * The adapted code block MUST be followed by a call to Ctx.end().
	 */
	public static void withActiveLayer(Layer l){
		
		ArrayList<Layer> top = activeLayers.get().peek();
		ArrayList<Layer> newTop = (ArrayList<Layer>) top.clone();
		if(top.contains(l)){
			Stack<ArrayList<Layer>> tmp = activeLayers.get();
			tmp.push(newTop);
			activeLayers.set(tmp);
		}else{
			newTop.add(0,l);
			Stack<ArrayList<Layer>> tmp = activeLayers.get();
			tmp.push(newTop);
			activeLayers.set(tmp);
		}
		LayerWrapperManager.setActiveLayers(newTop);
		return;
	}
	
	
	/**
	 * 
	 * @param l
	 */
	public static void withoutLayer(Layer l){
		ArrayList<Layer> top = activeLayers.get().peek();
		ArrayList<Layer> newTop = (ArrayList<Layer>) top.clone();
		newTop.remove(l);
		
		Stack<ArrayList<Layer>> tmp = activeLayers.get();
		tmp.push(newTop);
		activeLayers.set(tmp);
			
		LayerWrapperManager.setActiveLayers(newTop);
	}
	
	
	/**
	 * Closes the scope of a layer activation. This call MUST appear INSIDE THE
	 * SAME CODE BLOCK to close the scope of a with/without directive.
	 */
	public static void end(){
		
		//activeLayers.pop();
		Stack<ArrayList<Layer>> tmp = activeLayers.get();
		tmp.pop();
		activeLayers.set(tmp);
	
		LayerWrapperManager.setActiveLayers(activeLayers.get().peek());
	}
	
	/*
	public static Stack<ArrayList<Layer>> getAllActiveLayers(){
		return activeLayers;
	}*/
	
	
	
	// Test only TODO: make private and access trough reflection
	public static void clear(){
		//activeLayers.push(new ArrayList<Layer>());
		Stack<ArrayList<Layer>> tmp = activeLayers.get();
		tmp.push(new ArrayList<Layer>());
		activeLayers.set(tmp);
	}

}




